<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <script type="text/javascript" src="../public/cdn/vue/2.6.10/vue.js"></script>
        <script type="text/javascript" src="../public/cdn/vuex/3.1.1/vuex.min.js"></script>
    </head>

    <body>
        <div id="app">
            <p>$store.state.count: {{ $store.state.count }}</p>
            <p>$store.getters.myCount: {{ $store.getters.myCount }}</p>
            <p>$store.getters.printCount('hello world'): {{ $store.getters.printCount('hello world') }}</p>
            <button @click="myIncrement">myIncrement</button>
            <button @click="increment">increment</button>
            <button @click="sleepOneSecAndThenIncrement">sleepOneSecAndThenIncrement</button>
        </div>
    </body>

    <script>
        const store = new Vuex.Store({
            state: {
                count: 0
            },
            getters: { // getter 的返回值会根据它的依赖被缓存起来，且只有当它的依赖值发生了改变才会被重新计算
                myCount (state, getters) {
                    console.log('getters:', getters); // 可以接受其他 getter 作为第二个参数 来访问其他getter的数据
                    return 'count的值为:' + state.count
                },
                printCount (state) {
                    // 当getter返回的是一个函数时，能实现给getter传参
                    return function (msg) {
                        return 'count的值为:' + state.count + '; msg为:' + msg;
                    }
                }
            },
            // mutations用来改变state状态
            // 第一个参数是state对象，第二个参数payload能传输额外的参数
            mutations: {
                increment (state, payload) {
                    state.count++;
                    console.log(payload);
                }
            },
            // 通过 actions 来分发 mutation，从而间接改变 state
            // 为啥不直接用mutations而要用actions呢？因为mutations只能同步修改state，而actions可以异步修改state
            // 这里的{commit}是ES2015 的 参数解构 写法，能简化代码；就是能更方便地访问第一个参数的commit方法
            actions: {
                incrementTwice (state, payload) {
                    state.commit('increment', payload);
                    state.commit('increment', payload);
                },
                // 上面的方法等同于下面的代码
                incrementTwice2 ({ commit }, payload) {
                    commit('increment', payload);
                    commit('increment', payload);
                },
                sleepOneSecAndThenIncrement ({ commit }, payload) {
                    // store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise，调用store.dispatch会返回Promise，通过它我们能知道什么时候执行完
                    return new Promise((resolve, reject) => {
                        setTimeout(() => {
                            commit('increment', payload);
                            resolve()
                        }, 1000)
                    })
                }
            },
        })
        // 为了在组件中通过this.$store访问store，需要在Vue实例上注入store
        const app = new Vue({
            el: '#app',
            store,  // 当支持es6时，key和value同名时，可以简写
            methods: {
                myIncrement () {
                    // 提交mutation；为了更明确地追踪到状态的变化，只允许通过mutation的方式修改状态
                    // 方式一
                    // this.$store.commit('increment', { msg: 'hello world' })
                    // 方式二：通过type指定mutation，整个对象都会作为payload传给mutation
                    this.$store.commit({
                        type: 'increment',
                        msg: 'hello world'
                    })
                    // this.$store.state.count = 11; // TODO 这样也能改，为啥不建议
                },
                ...Vuex.mapMutations(['increment']), // 快捷添加提交mutations的方法；等同于添加了个increment() { this.$store.commit('increment') }
                sleepOneSecAndThenIncrement () {
                    // 提交action
                    this.$store.dispatch('sleepOneSecAndThenIncrement', { msg: 'hello world' }).then(() => {
                        alert('执行完毕')
                    })
                },
            }
        });
    </script>

</html>
